<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>可展开关闭的目录树</title>
		<style>
			body {
				font-size: 12px;
				color: stroke:rgb(51, 136, 255) !important;
			}

			.company_content {
				border: 1px solid rgb(51, 136, 255);
				transition: x 2s, y 2s;
			}
		</style>
	</head>
	<body>
		<!-- <button type="button" onclick="open()">展开/关闭</button> -->
		<svg id="s" width="3700" height="3700" style="overflow-x: scroll;" version="1.1" baseProfile="full"
			xmlns="http://www.w3.org/2000/svg" style="background: #fff">

			<defs>
				<marker fill="rgb(51, 136, 255)" id="arrowhead" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6"
					markerHeight="6" orient="auto">
					<path d="M 0 0 L 10 5 L 0 10 z" />
				</marker>
			</defs>
		</svg>

	</body>
</html>
<script src="data.js" type="text/javascript" charset="utf-8"></script>
<script src="data/data.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
	//公司之间的水平边距
	var levelSeparate = 40;
	//公司之间的垂直边距
	var verticalSeparate = 30;
	//公司宽度
	var companyWidth = 56;
	//公司高度
	var companyHeight = 180;
	var status = false;
	//公司主体内容宽度
	var companyContentWidth = 40;
	//目录树根
	var companyTree;

	class Svg {
		constructor(el) {
			this.svg = document.getElementById("s");
		}

		append(element) {
			this.svg.appendChild(element.toElement())
		}
	}

	var SVG_NS = "http://www.w3.org/2000/svg";
	var svg = new Svg("s");

	class Polyline {
		constructor(startX, startY, endX, endY, company) {
			this.element = document.createElementNS(SVG_NS, "polyline")
			this.startX = startX;
			this.startY = startY;
			this.endX = endX;
			this.endY = endY;
			this.company = company;

			this.element.setAttribute("points", this._points(this.startX, this.startY, this.endX, this.endY))
			this.element.setAttribute("style", "fill:none;stroke:rgb(51, 136, 255);stroke-width:1");
			this.element.setAttribute("fill-opacity", "0")
			this.element.setAttribute("marker-end", "url(#arrowhead)")
		}

		close() {
			this.element.setAttribute("style", "fill:none;stroke:rgb(51, 136, 255);stroke-width:1;display:none")
		}
		open() {
			this.element.setAttribute("style", "fill:none;stroke:rgb(51, 136, 255);stroke-width:1display:block")
		}

		_points(startX, startY, endX, endY) {
			let linePoint1 = startX + "," + startY;
			let linePoint2 = startX + "," + (startY + (verticalSeparate / 2));
			let linePoint3 = (endX + (this.company.getDisplayWidth() / 2)) + "," + (startY + (verticalSeparate / 2));
			let linePoint4 = (endX + (this.company.getDisplayWidth() / 2)) + "," + endY;
			return linePoint1 + " " + linePoint2 + " " + linePoint3 + " " + linePoint4;
		}

		updateX(x) {
			//获取最新的位置和宽度，重新和父元素连接
			let xs = this.company.getParent().getX() + (this.company.getParent().getWidth() / 2);
			let ys = this.company.getParent().getY() + companyHeight;
			let xe = this.company.getX();
			let ye = this.company.getY();
			this.element.setAttribute("points", this._points(xs, ys, xe, ye))
		}

		toElement() {
			return this.element;
		}
	}

	class Company {
		constructor(x, y, tree) {
			this.tree = tree;
			this.show = true;
			this.x = x;
			this.y = y;
			this.setWidth(this.tree.getWidth());
			this.height = companyHeight;
			this.width = this.tree.getWidth();
			this.g = document.createElementNS(SVG_NS, "g")
			this.g.setAttribute("transition", "x 2s, y 2s;")
			this.g.setAttribute("transform", "translate(" + x + "," + y + ")")
			this.foreignObject = null;
			this.g.appendChild(this._createHTML(tree.data.name))
			this.g.setAttribute("id", tree.data.name)
			svg.append(this)
			this.polyline = null;
			this.children = [];
			this.childrenX = this.x;
			this.installSwitch = false;
			this.swi = null;
			this.parent = null;
			this.childStatus = "open"

			if (this.tree.hasChildren()) {
				//渲染子元素
				for (var i = 0; i < this.tree.getChildrens().length; i++) {
					let tree = this.tree.getChildrens()[i]
					this.appendChild(tree);
				}

				//安装开关
				if (!this.installSwitch) {
					this.swi = new Switch(this);
					svg.append(this.swi)
					this.installSwitch = true;
				}
			}
		}

		close() {
			//隐藏公司
			this.g.setAttribute("style","display:none");
			//改变状态
			this.show = false;
			//隐藏连接线
			this.polyline.close();
			//隐藏开关
			if (this.swi) {
				this.swi.close();
			}
			//关闭子元素
			for (var i = 0; i < this.children.length; i++) {
				this.children[i].close()
			}
			
		}
		getChildrenStatus(){
			return this.childStatus;
		}
		open() {
			//显示公司
			this.g.setAttribute("style", "display:block")
			//改变状态
			this.show = true;
			this.childStatus = "open"
			//显示连接线
			this.polyline.open();
			//显示开关
			if (this.swi) {
				this.swi.open();
			}
			//关闭子元素
			for (var i = 0; i < this.children.length; i++) {
				this.children[i].open()
			}
		}

		setWidth(w) {
			this.width = w;
		}

		getWidth() {
			return parseInt(this.foreignObject.getAttribute("width").replace("px", ""));
		}

		isShow() {
			return this.show;
		}

		getDisplayChildren() {
			let cs = [];
			for (var i = 0; i < this.children.length; i++) {
				if (this.children[i].isShow()) {
					cs.push(this.children[i])
				}
			}
			return cs;
		}

		getDisplayWidth() {
			let w = 0;
			if (this.getDisplayChildren().length == 0) {
				return companyWidth;
			}
			for (var i = 0; i < this.getDisplayChildren().length; i++) {
				let displayChild = this.getDisplayChildren()[i];
				w += displayChild.getDisplayWidth();
			}
			return w;
		}

		getHeight() {
			return this.height;
		}

		_createRect() {
			let rect = document.createElementNS(SVG_NS, "rect");
			rect.setAttribute("width", companyWidth + "px")
			rect.setAttribute("height", companyHeight + "px")
			rect.setAttribute("fill", "#fff")
			rect.setAttribute("stroke-width", "1")
			rect.setAttribute("stroke", "rgb(0,0,0)");
			return rect;
		}

		_createHTML(txt) {
			this.foreignObject = document.createElementNS(SVG_NS, "foreignObject")
			this.foreignObject.setAttribute("class", "htmlContent")
			this.foreignObject.setAttribute("width", this.tree.getWidth() + "px")
			this.foreignObject.setAttribute("height", this.height + "px")

			let div = document.createElement("div")
			div.setAttribute("class", "company_content")
			div.setAttribute("style", "width:" + companyContentWidth + "px;margin:0 auto;height:" + (this.height - 2) +
				"px;1px solid rgb(51, 136, 255); overflow-y:auto;background:rgb(249, 252, 255);border-radius:3px;color:#333;text-align:center;"
			);
			div.innerHTML = "<span style='width:14px;writing-mode: vertical-rl;transform: rotate(360deg);'>" + txt +
				"</span>"

			this.foreignObject.appendChild(div);
			return this.foreignObject;
		}

		getX() {
			let s = this.g.getAttribute("transform")
			return parseInt(s.substring(s.indexOf("(") + 1, s.lastIndexOf(")")).split(",")[0]);
		}

		getY() {
			let s = this.g.getAttribute("transform")
			return parseInt(s.substring(s.indexOf("(") + 1, s.lastIndexOf(")")).split(",")[1]);
		}

		_updatePosition(x, y) {
			this.g.setAttribute("transform", "translate(" + x + "," + y + ")")
			this.x = x;
			this.y = y;
		}

		/**
		 * 偏移
		 * @param {Object} len 偏移的像素值
		 */
		offset(targetX) {
			//移动当前元素位置，并改变连接线位置
			this._updatePosition(targetX, this.y);
			//改变连接线位置
			this.polyline.updateX(targetX + (this.getWidth() / 2))
		}

		doUpdate() {
			//改变宽度
			let displayWidth = this.getDisplayWidth();
			this.foreignObject.setAttribute("width", displayWidth)
			//改变连接点位置
			if (this.swi) {
				this.swi.offset();
			}

			//移动子元素位置
			let displayChildren = this.getDisplayChildren();
			let x = this.getX();
			let y = this.getY() + companyHeight + verticalSeparate;
			let sx = this.getX();
			for (var i = 0; i < displayChildren.length; i++) {
				let displayChild = displayChildren[i]
				//改变子元素位置
				displayChild.offset(sx);
				sx += displayChild.getDisplayWidth();
			}
			// console.log("displayWidth("+this.tree.data.name+")",displayWidth)
		}

		_childrenStartX() {
			return this.x;
		}

		setPolyline(polyline) {
			this.polyline = polyline;
		}

		update() {
			this.doUpdate()
			//更新子元素
			for (var i = 0; i < this.children.length; i++) {
				let child = this.children[i]
				//子元素
				child.update()
			}
		}

		closeChild() {
			this.childStatus = "close"
			for (var i = 0; i < this.children.length; i++) {
				let c = this.children[i]
				c.close();
			}
		}
		
		openChild() {
			this.childStatus = "open"
			for (var i = 0; i < this.children.length; i++) {
				let c = this.children[i]
				c.open();
			}
		}

		setParent(parent) {
			this.parent = parent;
		}

		getParent() {
			return this.parent;
		}

		appendChild(tree) {
			//挂载子元素
			let x = this.childrenX;
			let y = this.y + companyHeight + verticalSeparate;
			let c = new Company(x, y, tree);
			c.setParent(this);
			this.children.push(c)
			svg.append(c)

			//画连接线
			let lineX = this.x + (this.width / 2);
			let lineY = this.y + companyHeight;
			let polyline = new Polyline(lineX, lineY, x, y, c)
			svg.append(polyline)
			c.setPolyline(polyline)

			this.childrenX += c.getWidth();
			return c;
		}

		toElement() {
			return this.g;
		}
	}

	class SwitchIcon {
		constructor(x, y) {
			//创建开关icon <text x="0" y="15" fill="red">I love SVG</text>
			this.text = document.createElementNS(SVG_NS, "text");
			this.text.setAttribute("x", x)
			this.text.setAttribute("y", y)
			this.text.setAttribute("fill", "rgb(51, 136, 255)")
			this.text.innerHTML = "-";
		}

		close() {
			this.text.setAttribute("style", "display:none")
		}

		toElement() {
			return this.text;
		}
	}

	class Switch {
		constructor(company) {
			this.r = 2;
			this.company = company;
			this.element = document.createElementNS(SVG_NS, "circle");
			this.element.setAttribute("cx", this.getX())
			this.element.setAttribute("cy", this.getY())
			this.element.setAttribute("r", 5)
			this.element.setAttribute("stroke", "rgb(51, 136, 255)")
			this.element.setAttribute("fill", "#fff");
			
			this.element.addEventListener("click", e => {
				console.log(this.company);
				if (this.company.getChildrenStatus()=="open") {
					this.company.closeChild();
				} else if(this.company.getChildrenStatus()=="close"){
					this.company.openChild();
				}
				//更新整个树
				companyTree.update()
			})
			this.switchIcon = new SwitchIcon(this.getX(), this.getY());
			svg.append(this.switchIcon)
		}

		close() {
			this.switchIcon.close();
			this.element.setAttribute("style", "display:none")
		}

		open() {
			this.element.setAttribute("style", "display:block")
		}

		offset() {
			let x = this.company.getX() + this.company.getDisplayWidth() / 2
			this.element.setAttribute("cx", x);
		}

		getX() {
			return this.company.getX() + (this.company.getWidth() / 2)
		}
		getY() {
			return this.company.getY() + (this.company.getHeight() - (parseFloat(this.r) / 2)) + 6
		}

		toElement() {
			return this.element;
		}
	}





	class Tree {
		constructor(data, x, y) {
			this.data = data;
			this.childrens = [];
			this.x = x;
			this.y = y;
			this.show = true;
		}

		setX(x) {
			this.x = x;
		}

		setY(y) {
			this.y = y;
		}

		getName() {
			return this.data.name;
		}

		/**
		 * 添加子元素
		 * @param {Tree} children 子元素
		 */
		addChildren(children) {
			this.childrens.push(children);
		}

		getChildrens() {
			return this.childrens;
		}

		hasChildren() {
			return this.getWidth() > companyWidth;
		}

		getWidth() {
			if (!this.childrens || this.childrens.length == 0) {
				return companyWidth;
			}

			let width = 0;
			for (var i = 0; i < this.childrens.length; i++) {
				let c = this.childrens[i];
				if (c.show) {
					width += c.getWidth();
				}
			}
			return width;
		}

	}



	function parseTree(tree, companys) {
		for (var i = 0; i < companys.length; i++) {
			let company = companys[i];
			let children = new Tree(company);
			tree.addChildren(children)
			if (company.children) {
				parseTree(children, company.children)
			}
		}
	}

	function renderTree(tree, x, y, parent) {
		if (!parent) {
			parent = new Company(x, y, tree.getName(), tree.getWidth())
		}
		for (var i = 0; i < tree.getChildrens().length; i++) {
			let ct = tree.getChildrens()[i]
			let c = parent.appendChild({
				name: ct.getName()
			}, ct.getWidth())
			if (ct.getChildrens().length > 0) {
				renderTree(ct, null, null, c)
			}
		}
	}

	function listToTree(arr) {
		let resArr = []
		arr.forEach(v => {
			// 找这个元素的 父亲(item.id === v.parentId)
			// 去arr中 中 找v的父亲，找到的话，把v添加到父亲的children属性中
			let parentNode = arr.find(item => item.name == v.parent_name);
			if (parentNode) {
				// 如果没有找到父亲-说明 他是一级目录
				if (parentNode['children']) {
					parentNode['children'].push(v);
				} else {
					parentNode['children'] = [v];
				}
			} else {
				//说明这个v 没有父亲
				// v 
				resArr.push(v)
			}
		});

		return resArr;
	}

	//根节点
	var rootX = 1800,
		rootY = 40;

	var x1 = 500,
		y1 = 500;
	var treeData = listToTree(data)[0];

	var tree = new Tree(treeData);
	parseTree(tree, treeData.children);

	console.log(tree)
	companyTree = new Company(100, 100, tree)
</script>
